<?php
/**
 * @version $Id: DirTree.php 26 2009-06-29 20:17:28Z joomla@nik-it.de $
 * @package		DirTree
 * @author		Nikolai Plath (elkuku) {@link http://www.nik-it.de NiK-IT.de}
 * @author		Created on 09-Feb-2009
 *
 * Install:
 * Add
 * require_once( "$IP/extensions/DirTree/DirTree.php" );
 * to your LocaleSettings.php
 *
 * Usage:
 * <dirtree>dir 1/dir 2/dir N/file.ext</dirtree>
 *
 * Required:
 * Have Fun =;)
 */

//--No direct access
defined('MEDIAWIKI') or die('=;)');

defined('DS') or define('DS', DIRECTORY_SEPARATOR);
defined('NL') or define('NL', "\n");

//Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW versions, as per r35980
if ( defined( 'MW_SUPPORTS_PARSERFIRSTCALLINIT' ) )
{
	$wgHooks['ParserFirstCallInit'][] = 'wfDirTree';
}
else
{ // Otherwise do things the old fashioned way
	$wgExtensionFunctions[] = 'wfDirTree';
}

$wgExtensionCredits['parserhook'][] = array(
	'version'     => '1.0',
	'name'        => 'DirTree',
	'author'      => array('Nikolai Plath'),
	'email'       => 'nik@',
	'url'         => 'http://www.mediawiki.org/wiki/Extension:DirTree...soon',
	'description' => 'Draws a directory tree by <tt>&lt;dirtree&gt;</tt> tags',
	);

function wfDirTree()
{
	global $wgParser, $wgHooks;
	$wgParser->setHook( 'dirtree', 'renderDirTree' );
	$wgHooks['OutputPageParserOutput'][] = 'fnDirTreeOutputHook';

	return true;
}//function

function renderDirTree($input, $argv, &$parser)
{
	global $IP;

	$dirTree = '';
	$sourceCode = '';

	$input = str_replace("\n", '', $input);
	$dirArr = explode('/', $input);
	$lines =( array_key_exists('lines', $argv) ) ? $argv['lines'] : false;
	$realPath = '';

	/*
	 * Draw folder tree
	 */
	$level = -1;
	foreach ($dirArr as $entry)
	{
		$indent = false;
		$color = 'blue';
		if( strpos($entry, '|'))
		{
			//--Found some options
			$optArr = explode('|', $entry);

			//--Separate the name
			$entry = array_shift($optArr);
			$options = array();
			foreach ($optArr as $opt)
			{
				if( strpos($opt, '='))
				{
					$oS = explode('=', $opt);
					$options[$oS[0]] = $oS[1];
				}
			}//foreach
			$indent =( array_key_exists('indent', $options) ) ? $options['indent'] : false;
			if( array_key_exists('color', $options) ) { $color = $options['color']; };
		}

		if( strpos($entry, '.'))
		{
			//--Found a file.. suppose
			$class = ' class="file file_'.substr($entry, strrpos($entry, '.') + 1 ).'"';
		}
		else
		{
			//--Found a folder
			$class = ' class="folder folder_'.$color.'"';
		}
		$level =( $indent ) ? $indent : $level + 1;
		$dirTree .= ' '.str_repeat('  ', $level).'<span'.$class.'>'.trim($entry).'</span>'.NL;
		$realPath .= $entry.'/';
	}//foreach

	$realPath = substr($realPath, 0, strlen($realPath)-1);

	if( array_key_exists('wikiinclude', $argv))
	{
		$result = dirTreeGetWikiPage($argv['wikiinclude'].'/'.$realPath, $lines);
		if( ! $result['text'] )
		{
			//--ERROR happend
			return '<strong style="color: red;">'.$result['error'].'</strong>';
		}
		$sourceCode = $result['text'];
	}

	if( array_key_exists('include', $argv))
	{
		$result = dirTreeGetSourceFromFile(dirname(__FILE__).DS.'includes'.DS.$argv['include'].DS.$realPath, $lines);
		if( ! $result['text'] )
		{
			//--ERROR happend
			return '<strong style="color: red;">'.$result['error'].'</strong>';
		}
		$sourceCode = $result['text'];
	}
	if( array_key_exists('wikiinclude', $argv)
	 || array_key_exists('include', $argv)
	)
	{
		//--Include GeSHI
		include_once($IP.DS.'extensions'.DS.'geshi'.DS.'geshi.php');

		//--Get GeSHI language from file extension
		$geshiLanguage = substr($realPath, strrpos($realPath, '.') + 1 );
		list($linesStart, $linesEnd) = explode(',', $lines);

		//--Setup GeSHI
		$geshi = new GeSHi(trim($sourceCode), $geshiLanguage, 'extensions/geshi/geshi');
        $geshi->set_line_style('background: #fcfcfc;', 'background: #f0f0f0;');
		$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 2);
		$geshi->start_line_numbers_at($linesStart);

		$sourceCode = str_replace("\n",'<br />', $geshi->parse_code());
	}

	//--Construct a table for output
	$table = '<table style="border: 1px solid #f2f2f2;" width="100%">';
	$table .= '<tr valign="top">';
	$table .= '<td width="10%"><pre>'.$dirTree.'</pre></td>';
	$table .= '<td>'.$sourceCode.'</td>';
	$table .= '</tr>';
	$table .= '</table>';

	return $table;
}//function

function dirTreeGetSourceFromFile($fullPath, $lines=false)
{
	//--Setup some caching
	static $files;
	if( ! is_array($files)) { $files = array(); }

	//--CHECK & CLEAN the path
	$fullPath = fnDirTreeCheckPath($fullPath);

	$result = array();
	$result['text'] = '';

	if( ! file_exists($fullPath))
	{
		//--Page NOT FOUND
		$result['error'] = 'File not found';
		return $result;
	}

	if( array_key_exists($fullPath, $files))
	{
		//--Cached
		$contents = $files[$fullPath];
	}
	else
	{
		//--Get the file
		$contents = file($fullPath);

		//--Cache it
		$files[$fullPath] = $contents;
	}

	if( ! $lines )
	{
		//--Get FULL file
		$result['text'] = implode(NL, $contents);
	}
	else
	{
		if( ! strpos($lines, ','))
		{
			$result['error'] = 'Lines: wrong format missing komma';

			return $result;
		}

		list($start, $end) = explode(',', $lines);

		//--Check line numbers
		if(    $start > $end
			|| $start == 0
			|| $end == 0
			|| $end > count($contents)
		)
		{
			$result['error'] = 'Lines: wrong format';

			return $result;
		}

		for($i = $start-1; $i <= $end-1; $i ++)
		{
			$result['text'] .= $contents[$i];//.NL;
		}//for
	}

	return $result;
}//function

/**
 * get a Wiki page by name
 *
 * @param string $name
 * @return array
 */
function dirTreeGetWikiPage($name, $lines=false)
{
	//--Setup some caching
	static $files;
	if( ! is_array($files)) { $files = array(); }

	if( array_key_exists($name, $files))
	{
		//--Cached
		$page = $files[$name];
	}
	else
	{
		//--Get the Wiki page
		$title = Title::newFromText( $name );
		$article = new Article($title);
		$page = $article->getContent();

		//--Cache it
		$files[$name] = $page;
	}

	$result = array();
	$result['text'] = '';

	//--DIRTY @todo proper check for page exists
	if( strpos($page, '(Diese Seite') !== false)
	{
		//--Page NOT FOUND
		$result['error'] = 'Page not found';
		return $result;
	}

	if( ! $lines )
	{
		//--Get FULL file
		$result['text'] = $page;
	}
	else
	{

		if( ! strpos($lines, ','))
		{
			$result['error'] = 'Lines: wrong format missing komma';

			return $result;
		}

		list($start, $end) = explode(',', $lines);

		$page = explode(NL, $page);

		//--Check line numbers
		if(    $start > $end
			|| $start == 0
			|| $end == 0
			|| $end > count($page)
		)
		{
			$result['error'] = 'Lines: wrong format';

			return $result;
		}

		for($i = $start - 1; $i <= $end; $i++)
		{
			$result['text'] .= $page[$i].NL;
		}//for
	}

	return $result;
}//function

/**
 * Entry point for the hook for printing JS and CSS:
 */
function fnDirTreeOutputHook( &$m_pageObj, $m_parserOutput )
{
	//--Add only once
	if( defined('DirTreeCssAdded') ) { return true; }
	define('DirTreeCssAdded', 1);

	global $wgScriptPath;

	//--CSS
	$m_pageObj->addLink(
    array(
      'rel'   => 'stylesheet',
      'type'  => 'text/css',
      'href'  => $wgScriptPath . '/extensions/DirTree/DirTree.css'
    	)
 	);

	return true;
}//function

/**
 * Checks for snooping outside of the file system root
 *
 * @param	string	$path A file system path to check
 * @return	string	A cleaned version of the path
 */
function fnDirTreeCheckPath($path)
{
	global $IP;
	if( strpos($path, '..') !== false )
	{
		echo ('Path::check Use of relative paths not permitted'); // don't translate
		exit();
	}
	$path = fnDirTreeCleanPath($path);
	if( strpos($path, $IP) !== 0 )
	{
		echo ('Path::check Snooping out of bounds @ '.$path); // don't translate
		exit();
	}

	return $path;
}//function

/**
 * Function to strip additional / or \ in a path name
 *
 * @static
 * @param	string	$path	The path to clean
 * @param	string	$ds		Directory separator (optional)
 * @return	string	The cleaned path
 */
function fnDirTreeCleanPath($path, $ds=DS)
{
	global $IP;
	$path = trim($path);

	if (empty($path))
	{
		$path = $IP;
	}
	else
	{
		// Remove double slashes and backslahses and convert all slashes and backslashes to DS
		$path = preg_replace('#[/\\\\]+#', $ds, $path);
	}

	return $path;
}//function
